from fastapi import APIRouter, FastAPI, Query, File, UploadFile, HTTPException,Body
from fastapi import Request
from fastapi.templating import Jinja2Templates
from dotenv import load_dotenv
from fastapi.responses import StreamingResponse
from utils.audio2video import *
from utils.pptx_to_image import pptx_to_images
from utils.image_to_text import parse_image_to_text
from utils.audio_generate import *
from utils.movie_editor import *
import httpx
import uuid
import os
import traceback
from fastapi.staticfiles import StaticFiles
import oss2
from datetime import timedelta
import re
from fastapi.responses import StreamingResponse
import shutil
import asyncio
router = APIRouter()

UPLOAD_DIRECTORY = "files"
IMAGES_DIRECTORY = "images"
SCRIPTS_DIRECTORY = "scripts"

if not os.path.exists(UPLOAD_DIRECTORY):
    os.makedirs(UPLOAD_DIRECTORY)

app = FastAPI()


templates = Jinja2Templates(directory="templates")
load_dotenv()

API_KEY = os.getenv("DASHSCOPE_API_KEY")
ALLOWED_EXTENSIONS = {"pptx"}
MAX_FILE_SIZE = 20 * 1024 * 1024  # 20 MB



def clean_directory(directory):
    """删除指定目录下的所有文件"""
    for filename in os.listdir(directory):
        file_path = os.path.join(directory, filename)
        try:
            if os.path.isfile(file_path) or os.path.islink(file_path):
                os.unlink(file_path)
            elif os.path.isdir(file_path):
                shutil.rmtree(file_path)
        except Exception as e:
            print(f"Failed to delete {file_path}. Reason: {e}")


async def save_file(upload_file: UploadFile) -> str:
    file_extension = upload_file.filename.split(".")[-1]
    unique_filename = uuid.uuid4()
    file_name = f"{unique_filename}.{file_extension}"
    file_path = os.path.join(UPLOAD_DIRECTORY, file_name)
    with open(file_path, "wb") as buffer:
        while True:
            data = await upload_file.read(65536)  # 读取文件数据块
            if not data:
                break
            buffer.write(data)  # 写入文件数据块
    return file_path

def upload_to_oss(local_file_path: str, object_name: str):
    # 从环境变量中读取OSS配置
    OSS_ENDPOINT = os.getenv('OSS_ENDPOINT')
    OSS_BUCKET_NAME = os.getenv('OSS_BUCKET_NAME')
    auth = oss2.Auth(os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'))
    bucket = oss2.Bucket(auth, OSS_ENDPOINT, OSS_BUCKET_NAME)
    bucket.put_object_from_file(object_name, local_file_path)

    # 生成预签名URL
    expires_time = timedelta(hours=10)  # 预签名URL的有效期为1小时
    expires_in_seconds = int(expires_time.total_seconds())  # 将timedelta转换为秒数
    signed_url = bucket.sign_url('GET', object_name, expires_in_seconds)
    return signed_url

"""
导入文件生成图片
"""
@router.post("/api/outline")
async def outline(file: UploadFile = File(...)):

    # 校验文件扩展名
    file_extension = file.filename.split(".")[-1].lower()
    if file_extension not in ALLOWED_EXTENSIONS:
        raise HTTPException(status_code=400,
                            detail=f"支持的文件扩展名为 {', '.join(ALLOWED_EXTENSIONS)}")

    # 校验文件大小
    file_content = await file.read(MAX_FILE_SIZE + 1)  # 读取文件内容并限制最大大小
    if len(file_content) > MAX_FILE_SIZE:
        raise HTTPException(status_code=400, detail="本示例应用建议上传的pptx在20M以内，以便快速体验效果")

    # 生成UUID并创建目录
    unique_id = str(uuid.uuid4())
    unique_upload_directory = os.path.join(UPLOAD_DIRECTORY, unique_id)
    unique_images_directory = os.path.join(unique_upload_directory, IMAGES_DIRECTORY)
    os.makedirs(unique_images_directory, exist_ok=True)

    file_path = os.path.join(UPLOAD_DIRECTORY, file.filename)
    with open(file_path, "wb") as buffer:
        buffer.write(file_content)  # 写入文件内容

    # 调用 pptx_to_images 将 PPT 文件转换为图片
    pptx_to_images(file_path, unique_images_directory,20)

    # 获取所有生成的图片文件路径
    png_files = [f for f in os.listdir(unique_images_directory) if f.endswith('.png')]

    # 存储每个图片的OSS URL
    oss_urls = {}
    for png_file in png_files:
        match = re.match(r'slide_(\d+)\.png', png_file)
        if match:
            slide_number = match.group(1)
            png_local_path = os.path.join(unique_images_directory, png_file)
            png_object_name = f"{unique_images_directory}/{png_file}"
            oss_url = upload_to_oss(png_local_path, png_object_name)
            oss_urls[slide_number] = oss_url

    return {"message": "文件已成功接收并处理", "file_path": unique_id, "oss_urls": oss_urls}

@router.post("/api/generate-script")
async def generate_script(request: Request, payload: dict = Body(...)):
    oss_url = payload.get('oss_url','')
    file_path = payload.get('file_path','')
    index = payload.get('index',"1")
    if  oss_url == '' or file_path == '':
        raise HTTPException(status_code=400, detail="参数不能为空")

    # 获取文本理解和深度理解参数
    understanding_level = payload.get('understanding_level', 'text-understanding')

    # 确保 scripts 目录存在
    unique_upload_directory = os.path.join(UPLOAD_DIRECTORY, file_path)
    unique_scripts_directory = os.path.join(unique_upload_directory, SCRIPTS_DIRECTORY)
    if not os.path.exists(unique_scripts_directory):
        os.makedirs(unique_scripts_directory)

    flag, parsed_text = parse_image_to_text(oss_url,understanding_level)
    if not flag:
        raise HTTPException(status_code=471, detail=parsed_text)

    # 将生成的脚本保存到 scripts 目录中，文件名为 slide_x.md
    md_filename = "slide_" + index + ".md"
    md_filepath = os.path.join(unique_scripts_directory, md_filename)

    save_sentences_to_markdown(parsed_text, unique_scripts_directory,index)

    with open(md_filepath, "w") as md_file:
        md_file.write(parsed_text)

    return {"message": "文案生成成功！", "scripts": parsed_text}


@router.post("/api/generate-script-list")
async def generate_script(request: Request, payload: dict = Body(...)):
    # 获取OSS URLs
    oss_urls = payload.get('oss_urls', {})
    file_path = payload.get('file_path', '')
    if not oss_urls:
        raise HTTPException(status_code=490, detail="OSS为空")
    understanding_level = payload.get('understanding_level', 'text-understanding')

    # 确保 scripts 目录存在
    unique_upload_directory = os.path.join(UPLOAD_DIRECTORY, file_path)
    unique_scripts_directory = os.path.join(unique_upload_directory, SCRIPTS_DIRECTORY)

    # 定义一个函数来提取文件名中的数字部分
    def extract_number(filename):
        match = re.search(r'slide_(\d+)\.png', filename)
        return int(match.group(1)) if match else float('inf')

    # 按照提取的数字部分升序排列
    sorted_oss_urls = sorted(oss_urls.items(), key=lambda item: extract_number(item[0]))

    script_data = []

    for png_file, oss_url in sorted_oss_urls:
        try:
            parsed_text = parse_image_to_text(oss_url,understanding_level)

            # 将生成的脚本保存到 scripts 目录中，文件名为 slide_x.md
            md_filename = os.path.splitext(png_file)[0] + ".md"
            md_filepath = os.path.join(unique_scripts_directory, md_filename)

            save_sentences_to_markdown(parsed_text, unique_scripts_directory, extract_number(png_file))

            with open(md_filepath, "w") as md_file:
                md_file.write(parsed_text)

            script_data.append({"image_url": oss_url, "text": parsed_text})
        except Exception as e:
            traceback.print_exc()
            print(e)

    return {"message": "文案生成成功！", "scripts": script_data}

def save_sentences_to_markdown(text: str, output_directory: str, index1: int):
    """
    将文本按照换行符、句号、问号、感叹号拆分，并保存到单独的Markdown文件中。

    :param text: 输入的文本
    :param output_directory: 存储Markdown文件的主目录
    :param index1: 用于创建二级目录的索引
    """
    # 构建二级目录名称
    secondary_dir_name = f'paragraph_{index1}'

    # 完整的输出路径
    full_output_directory = os.path.join(output_directory, secondary_dir_name)

    # 创建输出目录（如果不存在）
    if not os.path.exists(full_output_directory):
        os.makedirs(full_output_directory)

    # 使用正则表达式拆分文本
    sentences = re.split(r'[；。\n！？]+', text)

    # 去除空字符串并去除前后空格
    sentences = [sentence.strip() for sentence in sentences if sentence.strip()]

    # 将每个句子保存到单独的Markdown文件中
    for i, sentence in enumerate(sentences):
        file_path = os.path.join(full_output_directory, f"sentence_{i + 1}.md")
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(sentence)


@router.post("/api/save-script")
async def save_script(payload: dict = Body(...)):
    index = payload.get('index',"")
    text = payload.get('text',"")
    file_path = payload.get('file_path',"")
    if  index == '' or file_path == '' or text == '':
        raise HTTPException(status_code=400, detail="参数不能为空")

    unique_upload_directory = os.path.join(UPLOAD_DIRECTORY, file_path)
    unique_scripts_directory = os.path.join(unique_upload_directory, SCRIPTS_DIRECTORY)
    # 构建文件名和路径
    md_filename = f"slide_{index}.md"
    md_filepath = os.path.join(unique_scripts_directory, md_filename)

    # 删除旧的 markdown 文件
    if os.path.exists(md_filepath):
        os.remove(md_filepath)

    # 删除旧的 paragraph 目录
    secondary_dir_name = f'paragraph_{index}'
    secondary_dir_path = os.path.join(unique_scripts_directory, secondary_dir_name)
    if os.path.exists(secondary_dir_path):
        shutil.rmtree(secondary_dir_path)

    # 保存新的 markdown 文件
    with open(md_filepath, "w", encoding='utf-8') as md_file:
        md_file.write(text)

    # 重新生成 paragraph 目录内容
    save_sentences_to_markdown(text, unique_scripts_directory, index)

    return {"message": "文案保存成功"}

@router.post("/api/generate-audio")
async def generate_audio(payload: dict = Body(...)):
    voice_type = payload.get('voice_type', "male")
    file_path = payload.get('file_path', "")

    unique_upload_directory = os.path.join(UPLOAD_DIRECTORY, file_path)
    unique_scripts_directory = os.path.join(unique_upload_directory, SCRIPTS_DIRECTORY)
    # 检查目录是否存在
    if not os.path.exists(unique_scripts_directory):
        raise HTTPException(status_code=404, detail="指定的目录不存在")
    # 合成 Markdown 文本为语音
    synthesize_md_to_speech(unique_scripts_directory, voice_type=voice_type)
    return {"message": "音频生成成功"}


@router.post("/api/generate-srt")
async def generate_srt(payload: dict = Body(...)):
    file_path = payload.get('file_path', "")
    unique_upload_directory = os.path.join(UPLOAD_DIRECTORY, file_path)
    unique_scripts_directory = os.path.join(unique_upload_directory, SCRIPTS_DIRECTORY)
    # 检查目录是否存在
    if not os.path.exists(unique_scripts_directory):
        raise HTTPException(status_code=404, detail="指定的目录不存在")
    generate_srt_from_audio(unique_scripts_directory, unique_scripts_directory)
    return {"message": "字幕生成成功"}


@router.post("/api/generate-video")
async def generate_video(payload: dict = Body(...)):
    file_path = payload.get('file_path', "")
    unique_upload_directory = os.path.join(UPLOAD_DIRECTORY, file_path)
    unique_scripts_directory = os.path.join(unique_upload_directory, SCRIPTS_DIRECTORY)
    unique_image_directory = os.path.join(unique_upload_directory, IMAGES_DIRECTORY)
    # 检查目录是否存在
    if not os.path.exists(unique_scripts_directory):
        raise HTTPException(status_code=404, detail="指定的目录不存在")

    # 计算音频持续时间
    audio_durations = calculate_audio_durations(unique_scripts_directory)

    # 获取所有符合条件的图片，并按文件名中的数字排序
    pattern = r'^slide_(\d+)\.png$'
    image_files = [
        f"{unique_image_directory}/{file}"
        for file in os.listdir(unique_image_directory)
        if re.match(pattern, file)
    ]
    image_files = natsort.natsorted(image_files, key=lambda x: int(re.match(pattern, os.path.basename(x)).group(1)))


    fps = 6

    # 创建一个临时目录来存储每一页生成的视频
    temp_video_directory = os.path.join(unique_upload_directory, "videos")
    os.makedirs(temp_video_directory, exist_ok=True)

    semaphore = asyncio.Semaphore(4)  # 设置并发数

    async def process_page(file, duration, index):
        async with semaphore:
            # 生成每一页的视频
            page_video_path = os.path.join(temp_video_directory, f"slide_{index}.mp4")
            await image_to_video(file, duration, fps, f"slide_{index}", temp_video_directory)

            # 获取对应的音频文件路径
            audio_file_path = os.path.join(unique_scripts_directory, f"slide_{index}.mp3")
            # 获取对应的字幕文件路径
            srt_file_path = os.path.join(unique_scripts_directory, f"slide_{index}.srt")

            # 合并音频和字幕到视频
            page_video_with_audio_subtitle_path = os.path.join(temp_video_directory, f"slide_{index}_with_audio_subtitle.mp4")
            try:
                await merge_audio_video_subtitle(page_video_path, audio_file_path, srt_file_path,
                                                 page_video_with_audio_subtitle_path)
            except Exception as e:
                print(f"Error processing slide_{index}: {e}")
                raise

            return page_video_with_audio_subtitle_path

    # 创建任务列表
    tasks = [
        process_page(file, audio_durations[i], i + 1)
        for i, file in enumerate(image_files)
    ]

    # 并发执行任务
    page_videos_with_audio_subtitle = await asyncio.gather(*tasks,return_exceptions=True)

    for i, task_result in enumerate(page_videos_with_audio_subtitle):
        if isinstance(task_result, Exception):
            raise HTTPException(status_code=500, detail=f"Error processing slide_{i + 1}: {str(task_result)}")

    # 合并所有生成的视频
    final_video_path = os.path.join(unique_upload_directory, "final_video.mp4")
    concatenate_videos(page_videos_with_audio_subtitle, final_video_path)

    # 返回视频的相对路径
    video_local_path = os.path.join(unique_upload_directory, "final_video.mp4")
    # 生成视频的OSS对象名
    video_object_name = f"{unique_upload_directory}/final_video.mp4"
    # 上传视频到OSS
    video_oss_url = upload_to_oss(video_local_path, video_object_name)

    return {"message": "视频生成成功", "video_url": video_oss_url}

@router.get("/api/resources")
async def resources(file_path: str = Query(..., description="路径到上传文件的目录")):
    if not file_path:
        raise HTTPException(status_code=400, detail="file_path 参数不能为空")

    unique_upload_directory = os.path.join(UPLOAD_DIRECTORY, file_path)
    unique_scripts_directory = os.path.join(unique_upload_directory, SCRIPTS_DIRECTORY)

    # 检查目录是否存在
    if not os.path.exists(unique_scripts_directory):
        raise HTTPException(status_code=404, detail="指定的目录不存在")

    # 获取目录下所有 .mp3 和 .md 文件的路径
    mp3_files = [f for f in os.listdir(unique_scripts_directory) if f.endswith('.mp3')]
    md_files = [f for f in os.listdir(unique_scripts_directory) if f.endswith('.md')]

    # 创建一个字典来存储按 x 分组的文件路径
    files_by_index = {}

    for mp3_file in mp3_files:
        match = re.match(r'slide_(\d+)\.mp3', mp3_file)
        if match:
            index = match.group(1)
            if index not in files_by_index:
                files_by_index[index] = {}
            files_by_index[index]['mp3'] = os.path.join(unique_scripts_directory, mp3_file)

    for md_file in md_files:
        match = re.match(r'slide_(\d+)\.md', md_file)
        if match:
            index = match.group(1)
            if index not in files_by_index:
                files_by_index[index] = {}
            files_by_index[index]['md'] = os.path.join(unique_scripts_directory, md_file)

    # 将字典转换为列表
    result = [{"index": index, "mp3": files.get('mp3'), "md": files.get('md')} for index, files in files_by_index.items()]

    return {"message": "获取成功", "files": result}


@router.get("/api/heartbeat")
async def heartbeat():
    return {"message": "Heartbeat", "status": "OK"}

